use std::ffi::OsString;
use std::fmt::{Display, Formatter};
use std::io::Error;
use std::path::PathBuf;
use fuse3::Errno;
use xor_name::XorName;
use crate::metadata::Location;

#[derive(Debug)]
pub enum FileSystemError {
    TreeNodeExistsError(PathBuf),
    NodeNotFoundError(u64),
    NameNotFoundError(OsString),
    PathNotFoundError(PathBuf),
    NodeNotFileError(PathBuf),
    NodeNotDirError(PathBuf),
    RemoteNotFoundError(XorName),
    LocalFilesystemError(Error),
    RemoteConnectError,
    RemoteTimeoutError,
    UnknownFileError(u64),
    RemoteResponseError(u32),
    WrongNodeLocationError(Location),
    RemoteNotMatchError(XorName, XorName),
}

impl Display for FileSystemError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            FileSystemError::TreeNodeExistsError(p) => f.write_fmt(format_args!("TreeNodeExistsError: {}", p.display())),
            FileSystemError::NodeNotFoundError(id) => f.write_fmt(format_args!("NodeNotFoundError: {}", id)),
            FileSystemError::NameNotFoundError(name) => f.write_fmt(format_args!("NameNotFoundError: {}", name.to_str().unwrap())),
            FileSystemError::PathNotFoundError(p) => f.write_fmt(format_args!("PathNotFoundError: {}", p.display())),
            FileSystemError::NodeNotFileError(p) => f.write_fmt(format_args!("NodeNotFileError: {}", p.display())),
            FileSystemError::NodeNotDirError(p) => f.write_fmt(format_args!("NodeNotDirError: {}", p.display())),
            FileSystemError::RemoteNotFoundError(p) => f.write_fmt(format_args!("RemoteNotFoundError: {}", p)),
            FileSystemError::LocalFilesystemError(e) => f.write_fmt(format_args!("LocalFilesystemError: {}", e)),
            FileSystemError::RemoteConnectError => f.write_fmt(format_args!("RemoteConnectError")),
            FileSystemError::RemoteTimeoutError => f.write_fmt(format_args!("RemoteTimeoutError")),
            FileSystemError::UnknownFileError(fh) => f.write_fmt(format_args!("UnknownFileError: {}", fh)),
            FileSystemError::RemoteResponseError(code) => f.write_fmt(format_args!("RemoteResponseError: {}", code)),
            FileSystemError::WrongNodeLocationError(l) => f.write_fmt(format_args!("WrongRenameNodeError: {:?}", l)),
            FileSystemError::RemoteNotMatchError(w, e) => f.write_fmt(format_args!("RemoteNotMatchError: move {}, find {}", w, e)),
        }
    }
}

impl From<FileSystemError> for Errno {
    fn from(e: FileSystemError) -> Self {
        match e {
            FileSystemError::TreeNodeExistsError(_p) => Errno::new_exist(),
            FileSystemError::NodeNotFoundError(_id) => Errno::new_not_exist(),
            FileSystemError::NameNotFoundError(_id) => Errno::new_not_exist(),
            FileSystemError::PathNotFoundError(_p) => Errno::new_not_exist(),
            FileSystemError::NodeNotFileError(_p) => Errno::new_is_dir(),
            FileSystemError::NodeNotDirError(_p) => Errno::new_is_not_dir(),
            FileSystemError::RemoteNotFoundError(_p) => libc::EIO.into(),
            FileSystemError::LocalFilesystemError(_e) => libc::EIO.into(),
            FileSystemError::RemoteConnectError => libc::EIO.into(),
            FileSystemError::RemoteTimeoutError => libc::EIO.into(),
            FileSystemError::UnknownFileError(_fh) => Errno::new_not_exist(),
            FileSystemError::RemoteResponseError(_code) => libc::EIO.into(),
            FileSystemError::WrongNodeLocationError(_code) => Errno::new_not_exist(),
            FileSystemError::RemoteNotMatchError(_w, _f) => libc::EIO.into(),
        }
    }
}

impl From<std::io::Error> for FileSystemError {
    fn from(e: Error) -> Self {
        FileSystemError::LocalFilesystemError(e)
    }
}